package com.ruoyi.tzai.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.ruoyi.tzai.service.INewsProcessService;
import com.ruoyi.tzai.service.INewsArticleService;
import com.ruoyi.tzai.service.INewsContentService;
import com.ruoyi.tzai.service.INewsAudioService;
import com.ruoyi.tzai.service.IAIModelConfigService;
import com.ruoyi.tzai.service.ITTSEngineConfigService;
import com.ruoyi.tzai.service.ai.IAIService;
import com.ruoyi.tzai.service.ai.AIServiceFactory;
import com.ruoyi.tzai.service.tts.ITTSService;
import com.ruoyi.tzai.service.tts.TTSServiceFactory;
import com.ruoyi.tzai.domain.NewsArticle;
import com.ruoyi.tzai.domain.NewsContent;
import com.ruoyi.tzai.domain.NewsAudio;
import com.ruoyi.tzai.domain.TTSEngineConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.tzai.service.IFileUploadService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.x.file.storage.core.FileInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import com.ruoyi.tzai.domain.AIModelConfig;
import com.ruoyi.tzai.domain.ApiKeyPool;
import com.ruoyi.tzai.domain.TaskQueue;
import com.ruoyi.tzai.domain.TaskStep;
import com.ruoyi.tzai.service.IApiKeyPoolService;
import com.ruoyi.tzai.service.ITaskQueueService;

/**
 * 资讯处理服务实现
 * 使用工厂模式从数据库动态获取AI和TTS服务
 *
 * @author tzai
 * @date 2025-10-27
 */
@Service
public class NewsProcessServiceImpl implements INewsProcessService
{
    private static final Logger log = LoggerFactory.getLogger(NewsProcessServiceImpl.class);

    @Autowired
    private INewsArticleService newsArticleService;

    @Autowired
    private INewsContentService newsContentService;

    @Autowired
    private INewsAudioService newsAudioService;

    @Autowired
    private IAIModelConfigService aiModelConfigService;

    @Autowired
    private ITTSEngineConfigService ttsEngineConfigService;

    @Autowired
    private AIServiceFactory aiServiceFactory;

    @Autowired
    private TTSServiceFactory ttsServiceFactory;

    @Autowired
    private IFileUploadService fileUploadService;

    @Autowired
    private IApiKeyPoolService apiKeyPoolService;

    @Autowired
    private ITaskQueueService taskQueueService;

    // 并行处理线程池
    private final ExecutorService parallelExecutor = Executors.newFixedThreadPool(10);

    @Override
    @Async
    public void processArticle(Long articleId) {
        log.info("========== 开始处理资讯 ========== ArticleID: {}", articleId);

        Long taskId = null;
        try {
            // 0. 检查是否存在未完成的任务（支持断点续传）
            TaskQueue existingTask = taskQueueService.selectTaskQueueByArticleId(articleId);
            if (existingTask != null && ("pending".equals(existingTask.getStatus()) || "running".equals(existingTask.getStatus()))) {
                log.info("发现未完成的任务，继续执行: TaskID={}, Status={}", existingTask.getTaskId(), existingTask.getStatus());
                resumeTask(existingTask.getTaskId());
                return;
            }
            
            // 0.1 获取文章信息（需要先获取文章以确定处理模式）
            log.info("[步骤0] 获取文章信息");
            NewsArticle article = newsArticleService.selectNewsArticleByArticleId(articleId);
            if (article == null) {
                log.error("[错误] 文章不存在: {}", articleId);
                updateArticleStatus(articleId, "4");
                return;
            }
            log.info("[步骤0] 文章标题: {}", article.getTitle());
            
            // 确定处理模式（在创建任务时就确定）
            String processMode = (article.getProcessMode() == null || article.getProcessMode().isEmpty()) ? "summary" : article.getProcessMode();
            log.info("[步骤0] 处理模式: {}", processMode);
            
            // 0.2 创建新任务记录
            taskId = createTask(articleId, processMode);
            if (taskId == null) {
                log.error("创建任务记录失败");
                updateArticleStatus(articleId, "4");
                return;
            }
            log.info("任务创建成功，TaskID: {}, 处理模式: {}", taskId, processMode);

            // 1. 更新状态为"AI处理中"
            log.info("[步骤1] 更新状态为 'AI处理中'");
            updateArticleStatus(articleId, "1");
            updateTaskStep(taskId, "clean_data", "running", null);

            // 1.5 清理旧数据（如果存在）
            log.info("[步骤1.5] 清理旧的内容和音频数据");
            cleanOldData(articleId);
            updateTaskStep(taskId, "clean_data", "completed", null);

            // 2. 处理文章内容
            log.info("[步骤2] 开始处理文章内容");
            updateTaskStep(taskId, "ai_translate", "running", null);

            // 获取用户输入的原始资讯内容
            String originalContent = article.getContent();
            if (originalContent == null || originalContent.trim().isEmpty()) {
                log.error("[错误] 资讯内容为空，无法处理 - ArticleID: {}", articleId);
                updateArticleStatus(articleId, "4");
                return;
            }

            // 清理文本内容（移除无效换行符、多余空格等）
            log.info("[步骤2.1] 清理文本内容，原始长度: {} 字符", originalContent.length());
            originalContent = cleanContentText(originalContent);
            log.info("[步骤2.1] 文本清理完成，清理后长度: {} 字符", originalContent.length());

            // 根据处理模式选择处理方式
            IAIService.BilingualSummary bilingualResult = null;
            
            if ("full".equals(processMode)) {
                // 全文模式：使用多个启用的AI模型并行处理
                log.info("[步骤3] 全文模式 - 使用多个AI模型并行翻译");
                bilingualResult = processWithMultipleModels(taskId, articleId, originalContent, processMode);
            } else {
                // 摘要模式：使用默认AI服务单线程处理
                log.info("[步骤3] 摘要模式 - 使用默认AI模型处理");
                IAIService aiService = aiServiceFactory.getDefaultService();
                if (aiService == null) {
                    log.error("[错误] 无法获取AI服务");
                    updateArticleStatus(articleId, "4");
                    return;
                }
                log.info("[步骤3] AI服务获取成功: {}", aiService.getModelName());
                bilingualResult = aiService.generateBilingualSummary(originalContent, "cn", processMode);
            }

            if (bilingualResult == null) {
                log.error("[错误] 双语内容生成失败");
                updateTaskStep(taskId, "ai_translate", "failed", "AI翻译失败");
                updateTaskStatus(taskId, "failed", "AI翻译失败");
                updateArticleStatus(articleId, "4");
                return;
            }

            log.info("[步骤3] 双语摘要生成成功");
            log.info("  - 中文摘要长度: {}", bilingualResult.getCnSummary() != null ? bilingualResult.getCnSummary().length() : 0);
            log.info("  - 英文摘要长度: {}", bilingualResult.getEnSummary() != null ? bilingualResult.getEnSummary().length() : 0);
            updateTaskStep(taskId, "ai_translate", "completed", null);

            // 4. 保存中英文内容
            log.info("[步骤4-5] 保存中英文内容...");
            updateTaskStep(taskId, "save_content", "running", null);
            saveTranslatedContent(articleId, article.getArticleType(), bilingualResult);
            updateTaskStep(taskId, "save_content", "completed", null);

            // 6. 更新状态为"TTS处理中"
            log.info("[步骤6] 更新状态为 'TTS处理中'");
            updateArticleStatus(articleId, "2");
            updateTaskStep(taskId, "tts_generate_cn", "running", null);

            // 7. 串行生成双语音频（避免并发导致的API速率限制）
            log.info("[步骤7] 串行生成中英文音频（先中文后英文）...");

            // 先生成中文音频
            boolean cnSuccess = false;
            try {
                log.info("开始生成中文音频...");
                cnSuccess = generateAudio(articleId, "cn", taskId);
                log.info("中文音频生成{}", cnSuccess ? "成功" : "失败");
                if (cnSuccess) {
                    updateTaskStep(taskId, "tts_generate_cn", "completed", null);
                } else {
                    updateTaskStep(taskId, "tts_generate_cn", "failed", "中文TTS生成失败");
                }
            } catch (Exception e) {
                log.error("中文音频生成异常", e);
                updateTaskStep(taskId, "tts_generate_cn", "failed", "中文TTS生成异常: " + e.getMessage());
            }

            // API冷却时间：等待10秒，避免中英文请求过于密集触发速率限制
            try {
                int cooldownSeconds = 10;
                log.info("等待{}秒API冷却时间，避免触发速率限制...", cooldownSeconds);
                Thread.sleep(cooldownSeconds * 1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("API冷却等待被中断");
            }

            // 再生成英文音频
            updateTaskStep(taskId, "tts_generate_en", "running", null);
            boolean enSuccess = false;
            try {
                log.info("开始生成英文音频...");
                enSuccess = generateAudio(articleId, "en", taskId);
                log.info("英文音频生成{}", enSuccess ? "成功" : "失败");
                if (enSuccess) {
                    updateTaskStep(taskId, "tts_generate_en", "completed", null);
                } else {
                    updateTaskStep(taskId, "tts_generate_en", "failed", "英文TTS生成失败");
                }
            } catch (Exception e) {
                log.error("英文音频生成异常", e);
                updateTaskStep(taskId, "tts_generate_en", "failed", "英文TTS生成异常: " + e.getMessage());
            }

            log.info("[步骤7] 音频生成完成 - 中文: {}, 英文: {}",
                     cnSuccess ? "成功" : "失败",
                     enSuccess ? "成功" : "失败");

            // 8. 更新状态为"已完成"
            log.info("[步骤8] 更新状态为 '已完成'");
            updateArticleStatus(articleId, "3");
            updateTaskStatus(taskId, "completed", null);

            log.info("========== 资讯处理完成 ========== ArticleID: {}, TaskID: {} ==========", articleId, taskId);

        } catch (Exception e) {
            log.error("========== 资讯处理失败 ========== ArticleID: {}, TaskID: {} ==========", articleId, taskId, e);
            // 更新状态为"处理失败"
            updateArticleStatus(articleId, "4");
            if (taskId != null) {
                updateTaskStatus(taskId, "failed", "处理异常: " + e.getMessage());
            }
        }
    }

    @Override
    public boolean generateSummary(Long articleId, String language) {
        try {
            NewsArticle article = newsArticleService.selectNewsArticleByArticleId(articleId);
            if (article == null) {
                return false;
            }

            // TODO: 获取文章内容并生成摘要
            return true;
        } catch (Exception e) {
            log.error("生成摘要失败", e);
            return false;
        }
    }

    @Override
    public boolean translateArticle(Long articleId, String fromLang, String toLang) {
        try {
            // 获取AI服务
            IAIService aiService = aiServiceFactory.getDefaultService();
            if (aiService == null) {
                log.error("无法获取AI服务");
                return false;
            }

            // 获取源语言内容
            NewsContent sourceContent = newsContentService.selectNewsContentByArticleIdAndLanguage(articleId, fromLang);
            if (sourceContent == null) {
                log.error("源语言内容不存在");
                return false;
            }

            // 翻译标题（预留功能，暂不保存）
            // NewsArticle article = newsArticleService.selectNewsArticleByArticleId(articleId);
            // String translatedTitle = aiService.translate(article.getTitle(), fromLang, toLang);

            // 翻译摘要
            String translatedSummary = aiService.translate(sourceContent.getSummary(), fromLang, toLang);

            // 翻译正文
            String translatedContent = aiService.translate(sourceContent.getContent(), fromLang, toLang);

            // 智能断句
            String sentences = aiService.smartSegmentation(translatedContent, toLang);

            // 保存目标语言内容
            NewsContent targetContent = new NewsContent();
            targetContent.setArticleId(articleId);
            targetContent.setLanguage(toLang);
            targetContent.setSummary(translatedSummary);
            targetContent.setContent(translatedContent);
            targetContent.setSentences(sentences);
            targetContent.setCreateTime(DateUtils.getNowDate());
            newsContentService.insertNewsContent(targetContent);

            return true;
        } catch (Exception e) {
            log.error("翻译文章失败", e);
            return false;
        }
    }

    @Override
    public boolean generateAudio(Long articleId, String language) {
        return generateAudio(articleId, language, null);
    }

    /**
     * 生成音频（带任务跟踪）
     */
    public boolean generateAudio(Long articleId, String language, Long taskId) {
        try {
            // 获取内容（包含AI断句后的sentences）
            NewsContent content = newsContentService.selectNewsContentByArticleIdAndLanguage(articleId, language);
            if (content == null) {
                log.error("内容不存在");
                return false;
            }

            // 解析断句JSON
            String sentencesJson = content.getSentences();
            if (sentencesJson == null || sentencesJson.isEmpty()) {
                log.error("内容缺少断句信息: articleId={}, language={}", articleId, language);
                return false;
            }

            JSONArray sentences = JSON.parseArray(sentencesJson);
            if (sentences == null || sentences.isEmpty()) {
                log.error("断句JSON解析失败或为空: articleId={}, language={}", articleId, language);
                return false;
            }

            log.info("开始生成音频，共{}个句子", sentences.size());
            
            // 🔥 加载已保存的句子TTS结果（断点续传）
            java.util.Map<Integer, SentenceAudioResult> savedSentences = loadSavedSentenceTTS(taskId, language);
            if (!savedSentences.isEmpty()) {
                log.info("🔥 发现 {} 个已生成的句子音频，将跳过这些句子", savedSentences.size());
            }

            // 获取默认TTS引擎配置
            TTSEngineConfig ttsEngine = ttsEngineConfigService.selectDefaultTTSEngineConfig();
            if (ttsEngine == null) {
                log.error("未配置默认TTS引擎");
                return false;
            }

            // 获取默认TTS引擎的所有可用Keys
            List<ApiKeyPool> availableKeys = apiKeyPoolService.acquireKeys(
                "tts_engine", 
                ttsEngine.getEngineId(), 
                10  // 最多获取10个Key
            );
            
            if (availableKeys == null || availableKeys.isEmpty()) {
                log.error("默认TTS引擎没有可用的API Key");
                return false;
            }
            
            log.info("从默认TTS引擎 {} 获取到 {} 个可用Key", ttsEngine.getEngineName(), availableKeys.size());

            // 并行处理TTS生成
            List<SentenceAudioResult> results = new ArrayList<>();
            
            if (availableKeys.size() == 1) {
                // 只有1个Key，串行处理
                log.info("只有1个可用Key，使用串行处理");
                results = generateAudioSerial(sentences, language, availableKeys.get(0), ttsEngine, taskId, savedSentences);
            } else {
                // 多个Key，并行处理
                log.info("有{}个可用Key，使用并行处理", availableKeys.size());
                results = generateAudioParallel(sentences, language, availableKeys, ttsEngine, taskId, savedSentences);
            }
            
            log.info("所有TTS任务已完成，成功生成{}个音频片段", results.size());

            if (results.isEmpty()) {
                log.error("所有句子TTS生成均失败");
                return false;
            }

            // 按句子顺序排序
            results.sort((a, b) -> Integer.compare(a.getIndex(), b.getIndex()));
            log.info("收集到{}个有效音频片段，开始合并", results.size());

            // 下载所有音频片段并解析真实时长
            List<byte[]> audioChunks = new ArrayList<>();
            List<Double> realDurations = new ArrayList<>();  // 存储真实时长
            long totalSize = 0;
            double totalDuration = 0.0;

            for (SentenceAudioResult result : results) {
                FileInfo fileInfo = JSON.parseObject(result.getResult().getFileMetadata(), FileInfo.class);
                byte[] audioData = fileUploadService.downloadFile(fileInfo);
                audioChunks.add(audioData);
                totalSize += audioData.length;

                // 从WAV文件中解析真实时长
                double realDuration = parseWavDuration(audioData);
                realDurations.add(realDuration);
                totalDuration += realDuration;

                log.info("下载音频片段{}: {} bytes, API时长={}s, 真实时长={}s",
                        result.getIndex() + 1, audioData.length,
                        result.getResult().getDuration(), realDuration);
            }

            // 合并所有音频
            log.info("开始合并{}个音频片段，总大小: {} bytes", audioChunks.size(), totalSize);
            byte[] mergedAudio = mergeAudioChunks(audioChunks);
            log.info("音频合并完成，最终大小: {} bytes", mergedAudio.length);

            // 上传合并后的音频（WAV格式）
            String fileName = UUID.randomUUID().toString() + ".wav";
            FileInfo finalFileInfo = fileUploadService.uploadAudio(
                new ByteArrayInputStream(mergedAudio),
                fileName,
                mergedAudio.length
            );

            log.info("合并音频上传成功: {}", finalFileInfo.getUrl());

            // 合并所有时间戳（使用真实时长）
            String mergedTimestamps = mergeTimestamps(results, sentences, realDurations);

            // 保存音频信息
            NewsAudio audio = new NewsAudio();
            audio.setArticleId(articleId);
            audio.setLanguage(language);
            audio.setAudioUrl(finalFileInfo.getUrl());
            audio.setFileMetadata(JSON.toJSONString(finalFileInfo));
            audio.setDuration(BigDecimal.valueOf(totalDuration));
            audio.setTimestamps(mergedTimestamps);
            audio.setTtsEngine(ttsEngine.getEngineCode());
            audio.setFileSize((long)mergedAudio.length);
            audio.setCreateTime(DateUtils.getNowDate());
            newsAudioService.insertNewsAudio(audio);

            log.info("音频记录保存成功");
            return true;
        } catch (Exception e) {
            log.error("生成音频失败", e);
            return false;
        }
    }

    /**
     * 从WAV文件字节数组中解析音频时长（秒）
     * WAV文件格式：
     * - 0-3: "RIFF"
     * - 4-7: 文件大小-8
     * - 8-11: "WAVE"
     * - 12-15: "fmt "
     * - 16-19: fmt chunk大小（通常是16）
     * - 20-21: 音频格式（1=PCM）
     * - 22-23: 声道数
     * - 24-27: 采样率
     * - 28-31: 字节率
     * - 32-33: 块对齐
     * - 34-35: 位深度
     * - 36-39: "data"
     * - 40-43: 音频数据大小
     */
    /**
     * 带重试机制的TTS生成
     *
     * @param ttsService TTS服务
     * @param text 文本内容
     * @param language 语言
     * @param maxRetries 最大重试次数
     * @return TTS结果
     */
    private ITTSService.TTSResult generateSpeechWithRetry(ITTSService ttsService, String text, String language, int maxRetries) {
        int attempt = 0;
        Exception lastException = null;

        while (attempt < maxRetries) {
            try {
                // 如果是重试，先等待一段时间（更长的指数退避以应对429限流）
                if (attempt > 0) {
                    int waitTime = (int) (Math.pow(2, attempt) * 2500); // 5秒, 10秒, 20秒...
                    log.info("TTS重试 {}/{}, 等待{}ms...", attempt + 1, maxRetries, waitTime);
                    Thread.sleep(waitTime);
                }

                // 尝试生成语音
                return ttsService.generateSpeech(text, language, null);

            } catch (RuntimeException e) {
                lastException = e;
                String errorMsg = e.getMessage();

                // 检查是否是429速率限制错误
                if (errorMsg != null && (errorMsg.contains("429") || errorMsg.contains("Throttling.RateQuota"))) {
                    log.warn("遇到速率限制(429)，准备重试 {}/{}", attempt + 1, maxRetries);
                    attempt++;
                } else {
                    // 其他错误直接抛出，不重试
                    log.error("TTS生成失败（非429错误）: {}", errorMsg);
                    throw e;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("重试等待被中断");
                return null;
            }
        }

        log.error("TTS生成失败，已达到最大重试次数({})", maxRetries);
        if (lastException != null) {
            throw new RuntimeException("TTS生成失败，已重试" + maxRetries + "次", lastException);
        }
        return null;
    }

    private double parseWavDuration(byte[] wavData) {
        try {
            if (wavData.length < 44) {
                log.warn("WAV文件太小，无法解析: {} bytes", wavData.length);
                return 0.0;
            }

            // 读取采样率（24-27字节，小端序）
            int sampleRate = ((wavData[27] & 0xFF) << 24) |
                           ((wavData[26] & 0xFF) << 16) |
                           ((wavData[25] & 0xFF) << 8) |
                           (wavData[24] & 0xFF);

            // 读取声道数（22-23字节，小端序）
            int channels = ((wavData[23] & 0xFF) << 8) | (wavData[22] & 0xFF);

            // 读取位深度（34-35字节，小端序）
            int bitsPerSample = ((wavData[35] & 0xFF) << 8) | (wavData[34] & 0xFF);

            // 读取音频数据大小（40-43字节，小端序）
            int dataSize = ((wavData[43] & 0xFF) << 24) |
                         ((wavData[42] & 0xFF) << 16) |
                         ((wavData[41] & 0xFF) << 8) |
                         (wavData[40] & 0xFF);

            // 计算时长（秒）= 数据大小 / (采样率 * 声道数 * 位深度/8)
            double duration = (double) dataSize / (sampleRate * channels * bitsPerSample / 8.0);

            log.debug("解析WAV: 采样率={}Hz, 声道={}, 位深={}, 数据大小={} bytes, 时长={} 秒",
                     sampleRate, channels, bitsPerSample, dataSize, duration);

            return Math.round(duration * 100) / 100.0;  // 保留2位小数
        } catch (Exception e) {
            log.error("解析WAV时长失败", e);
            return 0.0;
        }
    }

    /**
     * 合并多个WAV音频文件
     * WAV文件格式：前44字节是RIFF头，后面是音频数据
     * 合并策略：保留第一个文件的头，跳过后续文件的头，只拼接音频数据
     */
    private byte[] mergeAudioChunks(List<byte[]> chunks) throws Exception {
        if (chunks.isEmpty()) {
            return new byte[0];
        }

        if (chunks.size() == 1) {
            return chunks.get(0);
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // 第一个文件：完整写入（包含WAV头）
        byte[] firstChunk = chunks.get(0);
        baos.write(firstChunk);

        // 后续文件：跳过44字节的WAV头，只写入音频数据
        for (int i = 1; i < chunks.size(); i++) {
            byte[] chunk = chunks.get(i);
            if (chunk.length > 44) {
                // 跳过WAV头（前44字节），只拼接音频数据
                baos.write(chunk, 44, chunk.length - 44);
            }
        }

        byte[] mergedData = baos.toByteArray();

        // 更新WAV头中的文件大小信息
        // ChunkSize (字节4-7): 整个文件大小 - 8
        int chunkSize = mergedData.length - 8;
        mergedData[4] = (byte) (chunkSize & 0xFF);
        mergedData[5] = (byte) ((chunkSize >> 8) & 0xFF);
        mergedData[6] = (byte) ((chunkSize >> 16) & 0xFF);
        mergedData[7] = (byte) ((chunkSize >> 24) & 0xFF);

        // Subchunk2Size (字节40-43): 音频数据大小
        int dataSize = mergedData.length - 44;
        mergedData[40] = (byte) (dataSize & 0xFF);
        mergedData[41] = (byte) ((dataSize >> 8) & 0xFF);
        mergedData[42] = (byte) ((dataSize >> 16) & 0xFF);
        mergedData[43] = (byte) ((dataSize >> 24) & 0xFF);

        return mergedData;
    }

    /**
     * 合并时间戳（按顺序拼接，使用从WAV文件解析的真实时长）
     * 统一使用 word 字段存储句子内容，适配前端高亮同步需求
     *
     * @param results 句子音频结果列表
     * @param sentences 句子内容数组
     * @param realDurations 从WAV文件解析的真实时长列表（与results一一对应）
     * @return 时间戳JSON字符串
     */
    private String mergeTimestamps(List<SentenceAudioResult> results, JSONArray sentences, List<Double> realDurations) {
        JSONArray sentenceTimestamps = new JSONArray();
        double timeOffset = 0.0;

        for (int i = 0; i < results.size(); i++) {
            SentenceAudioResult result = results.get(i);
            int index = result.getIndex();
            String sentenceText = sentences.getJSONObject(index).getString("text");

            // 使用从WAV文件解析的真实时长，而不是API返回的duration
            double realDuration = realDurations.get(i);

            JSONObject sentenceTimestamp = new JSONObject();
            sentenceTimestamp.put("index", index);
            sentenceTimestamp.put("word", sentenceText);  // 使用word字段存储整句

            // 使用真实时长来计算时间范围
            sentenceTimestamp.put("start", Math.round(timeOffset * 100) / 100.0);
            sentenceTimestamp.put("end", Math.round((timeOffset + realDuration) * 100) / 100.0);

            log.debug("句子 {} 时间范围: start={}, end={}, 真实时长={}s (API时长={}s)",
                     index, sentenceTimestamp.getDouble("start"),
                     sentenceTimestamp.getDouble("end"), realDuration,
                     result.getResult().getDuration());

            sentenceTimestamps.add(sentenceTimestamp);

            // 累加当前句子的真实时长（用于下一个句子的时间偏移）
            timeOffset += realDuration;
        }

        log.info("生成句子级时间戳完成，共{}个句子，总时长: {}秒",
                 sentenceTimestamps.size(), Math.round(timeOffset * 100) / 100.0);

        return sentenceTimestamps.toJSONString();
    }

    /**
     * 句子音频结果（用于异步处理）
     */
    private static class SentenceAudioResult {
        private final int index;
        private final ITTSService.TTSResult result;

        public SentenceAudioResult(int index, ITTSService.TTSResult result) {
            this.index = index;
            this.result = result;
        }

        public int getIndex() {
            return index;
        }

        public ITTSService.TTSResult getResult() {
            return result;
        }
    }

    @Override
    public boolean reprocessArticle(Long articleId) {
        try {
            // 删除旧的内容和音频
            newsContentService.deleteNewsContentByArticleId(articleId);
            newsAudioService.deleteNewsAudioByArticleId(articleId);

            // 重新处理
            processArticle(articleId);
            return true;
        } catch (Exception e) {
            log.error("重新处理资讯失败", e);
            return false;
        }
    }

    /**
     * 更新文章状态
     */
    private void updateArticleStatus(Long articleId, String status) {
        NewsArticle article = new NewsArticle();
        article.setArticleId(articleId);
        article.setStatus(status);
        article.setUpdateTime(DateUtils.getNowDate());
        newsArticleService.updateNewsArticle(article);
    }

    /**
     * 清理旧数据（重新处理时删除旧的内容和音频）
     */
    private void cleanOldData(Long articleId) {
        try {
            newsContentService.deleteNewsContentByArticleId(articleId);
            newsAudioService.deleteNewsAudioByArticleId(articleId);
            log.info("旧数据清理完成");
        } catch (Exception e) {
            log.warn("清理旧数据时发生错误（可能是首次处理）: {}", e.getMessage());
        }
    }

    /**
     * 清理资讯内容文本
     * 解决文本中的无效换行符、多余空格等问题，确保AI处理和TTS生成的准确性
     *
     * 处理内容：
     * 1. 移除HTML标签（如果有）
     * 2. 统一换行符为 \n（Windows \r\n -> Unix \n）
     * 3. 移除多余的连续换行符（超过2个的压缩为2个，保留段落分隔）
     * 4. 移除行首行尾的空格
     * 5. 移除多余的连续空格（压缩为1个）
     * 6. 移除全角空格（中文内容常见问题）
     * 7. 去除全文首尾空白
     *
     * @param content 原始内容
     * @return 清理后的内容
     */
    private String cleanContentText(String content) {
        if (content == null || content.isEmpty()) {
            return content;
        }

        String cleaned = content;
        int originalLength = content.length();

        // 1. 移除HTML标签（如果有）
        cleaned = cleaned.replaceAll("<[^>]+>", "");

        // 2. 统一换行符（Windows \r\n -> Unix \n）
        cleaned = cleaned.replaceAll("\\r\\n", "\n");
        cleaned = cleaned.replaceAll("\\r", "\n");

        // 3. 移除多余的连续换行符（超过2个的压缩为2个，保留段落分隔）
        cleaned = cleaned.replaceAll("\n{3,}", "\n\n");

        // 4. 移除行首行尾的空格，并过滤空行
        String[] lines = cleaned.split("\n");
        StringBuilder result = new StringBuilder();
        for (String line : lines) {
            line = line.trim();
            if (!line.isEmpty()) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
        }
        cleaned = result.toString();

        // 5. 移除多余的连续空格（压缩为1个）
        cleaned = cleaned.replaceAll(" {2,}", " ");

        // 6. 移除全角空格（中文内容常见问题）
        cleaned = cleaned.replaceAll("　+", "");

        // 7. 去除全文首尾空白
        cleaned = cleaned.trim();

        int cleanedLength = cleaned.length();
        int removedChars = originalLength - cleanedLength;

        if (removedChars > 0) {
            log.info("文本清理详情：移除了 {} 个无效字符，清理比例: {:.2f}%",
                     removedChars, (removedChars * 100.0 / originalLength));
        } else {
            log.info("文本清理详情：内容无需清理");
        }

        return cleaned;
    }

    /**
     * 使用多个AI模型并行处理全文翻译
     */
    private IAIService.BilingualSummary processWithMultipleModels(Long taskId, Long articleId, String content, String processMode) {
        try {
            // 0. 加载已保存的块翻译结果（断点续传）
            java.util.Map<Integer, IAIService.BilingualSummary> savedChunks = loadSavedChunkTranslations(taskId);
            if (!savedChunks.isEmpty()) {
                log.info("🔥 发现 {} 个已翻译的块，将跳过这些块的翻译", savedChunks.size());
            }
            
            // 1. 获取所有启用的AI模型
            List<AIModelConfig> enabledModels = aiModelConfigService.selectAIModelConfigList(
                new AIModelConfig() {{ setStatus("1"); }}
            );
            
            if (enabledModels == null || enabledModels.isEmpty()) {
                log.error("没有启用的AI模型");
                return null;
            }
            
            log.info("找到 {} 个启用的AI模型", enabledModels.size());
            
            // 2. 获取所有可用的API Keys
            List<ApiKeyPool> availableKeys = new ArrayList<>();
            for (AIModelConfig model : enabledModels) {
                List<ApiKeyPool> keys = apiKeyPoolService.acquireKeys("ai_model", model.getModelId(), 10);
                if (keys != null && !keys.isEmpty()) {
                    availableKeys.addAll(keys);
                    log.info("从模型 {} 获取到 {} 个可用Key", model.getModelName(), keys.size());
                }
            }
            
            if (availableKeys.isEmpty()) {
                log.error("没有可用的API Key");
                return null;
            }
            
            log.info("共获取到 {} 个可用的API Key，开始并行翻译", availableKeys.size());
            
            // 3. 文本分块（按段落分割）
            List<String> chunks = splitTextIntoChunks(content, availableKeys.size());
            log.info("文本分成 {} 块，每块平均 {} 字符", chunks.size(), content.length() / chunks.size());
            
            // 4. 并行翻译每个块（跳过已保存的块）
            List<Future<IAIService.BilingualSummary>> futures = new ArrayList<>();
            for (int i = 0; i < chunks.size(); i++) {
                final int index = i;
                final String chunk = chunks.get(i);
                final ApiKeyPool keyPool = availableKeys.get(i % availableKeys.size());
                
                // 检查该块是否已经翻译过
                if (savedChunks.containsKey(index)) {
                    log.info("块 {}/{} 已存在翻译结果，跳过 ✓", index + 1, chunks.size());
                    // 使用CompletableFuture返回已保存的结果
                    futures.add(CompletableFuture.completedFuture(savedChunks.get(index)));
                    continue;
                }
                
                Future<IAIService.BilingualSummary> future = parallelExecutor.submit(() -> {
                    long startTime = System.currentTimeMillis();
                    try {
                        log.info("块 {}/{} 开始翻译（模型: {}, Key: {}, 长度: {} 字符）", 
                                index + 1, chunks.size(), 
                                keyPool.getResourceType() + "-" + keyPool.getResourceId(),
                                keyPool.getKeyName(), 
                                chunk.length());
                        
                        // 根据Key找到对应的模型
                        AIModelConfig model = enabledModels.stream()
                            .filter(m -> m.getModelId().equals(keyPool.getResourceId()))
                            .findFirst()
                            .orElse(null);
                        
                        if (model == null) {
                            log.error("块 {}/{} 找不到对应的模型", index + 1, chunks.size());
                            return null;
                        }
                        
                        log.info("块 {}/{} 使用模型: {}", index + 1, chunks.size(), model.getModelName());
                        
                        // 创建AI服务
                        long serviceStartTime = System.currentTimeMillis();
                        IAIService aiService = aiServiceFactory.getService(model.getModelId());
                        if (aiService == null) {
                            log.error("块 {}/{} 无法创建AI服务", index + 1, chunks.size());
                            return null;
                        }
                        log.info("块 {}/{} AI服务创建耗时: {}ms", index + 1, chunks.size(), 
                                System.currentTimeMillis() - serviceStartTime);
                        
                        // 翻译当前块
                        long translateStartTime = System.currentTimeMillis();
                        log.info("块 {}/{} 开始调用AI翻译...", index + 1, chunks.size());
                        IAIService.BilingualSummary result = aiService.generateBilingualSummary(chunk, "cn", processMode);
                        long translateTime = System.currentTimeMillis() - translateStartTime;
                        
                        if (result != null) {
                            log.info("块 {}/{} 翻译完成 ✓ 耗时: {}秒，中文: {}字，英文: {}字", 
                                    index + 1, chunks.size(), 
                                    translateTime / 1000,
                                    result.getCnContent() != null ? result.getCnContent().length() : 0,
                                    result.getEnContent() != null ? result.getEnContent().length() : 0);
                            
                            // 🔥 立即保存块的翻译结果到任务步骤（防止丢失）
                            saveChunkTranslation(taskId, index, result);
                        } else {
                            log.error("块 {}/{} 翻译失败 ✗ 返回null（耗时: {}秒）", 
                                    index + 1, chunks.size(), translateTime / 1000);
                        }
                        
                        return result;
                    } catch (Exception e) {
                        long totalTime = System.currentTimeMillis() - startTime;
                        log.error("块 {}/{} 翻译异常 ✗ 耗时: {}秒，错误: {}", 
                                index + 1, chunks.size(), totalTime / 1000, e.getMessage(), e);
                        return null;
                    } finally {
                        // 释放Key
                        apiKeyPoolService.releaseKey(keyPool.getKeyId());
                        long totalTime = System.currentTimeMillis() - startTime;
                        log.info("块 {}/{} 处理完成，总耗时: {}秒", index + 1, chunks.size(), totalTime / 1000);
                    }
                });
                
                futures.add(future);
            }
            
            // 5. 等待所有翻译完成并收集结果
            log.info("等待 {} 个翻译任务完成（最长等待5分钟）...", futures.size());
            List<IAIService.BilingualSummary> results = new ArrayList<>();
            for (int i = 0; i < futures.size(); i++) {
                try {
                    log.info("等待块 {}/{} 的翻译结果...", i + 1, futures.size());
                    long waitStartTime = System.currentTimeMillis();
                    IAIService.BilingualSummary result = futures.get(i).get(5, TimeUnit.MINUTES);
                    long waitTime = System.currentTimeMillis() - waitStartTime;
                    
                    if (result != null) {
                        results.add(result);
                        log.info("块 {}/{} 结果已收集 ✓ (等待耗时: {}秒)", i + 1, futures.size(), waitTime / 1000);
                    } else {
                        log.error("块 {}/{} 返回null结果 ✗ (等待耗时: {}秒)", i + 1, futures.size(), waitTime / 1000);
                    }
                } catch (TimeoutException e) {
                    log.error("块 {}/{} 处理超时（超过5分钟）✗", i + 1, futures.size());
                } catch (Exception e) {
                    log.error("块 {}/{} 处理异常 ✗ 错误: {}", i + 1, futures.size(), e.getMessage(), e);
                }
            }
            
            if (results.isEmpty()) {
                log.error("所有块翻译失败");
                return null;
            }
            
            log.info("成功翻译 {}/{} 块", results.size(), chunks.size());
            
            // 6. 合并所有翻译结果
            return mergeTranslationResults(results);
            
        } catch (Exception e) {
            log.error("多模型并行处理异常", e);
            return null;
        }
    }
    
    /**
     * 将文本分块（按段落分割，尽量平均）
     */
    private List<String> splitTextIntoChunks(String content, int numChunks) {
        List<String> chunks = new ArrayList<>();
        
        // 按段落分割（保留段落结构）
        String[] paragraphs = content.split("\n+");
        
        if (paragraphs.length <= numChunks) {
            // 段落数少于或等于块数，每段一块
            for (String para : paragraphs) {
                if (!para.trim().isEmpty()) {
                    chunks.add(para.trim());
                }
            }
        } else {
            // 段落数多于块数，平均分配
            int parasPerChunk = paragraphs.length / numChunks;
            int remainder = paragraphs.length % numChunks;
            
            int startIndex = 0;
            for (int i = 0; i < numChunks; i++) {
                int chunkSize = parasPerChunk + (i < remainder ? 1 : 0);
                StringBuilder chunkBuilder = new StringBuilder();
                
                for (int j = 0; j < chunkSize && startIndex < paragraphs.length; j++) {
                    if (chunkBuilder.length() > 0) {
                        chunkBuilder.append("\n");
                    }
                    chunkBuilder.append(paragraphs[startIndex++].trim());
                }
                
                if (chunkBuilder.length() > 0) {
                    chunks.add(chunkBuilder.toString());
                }
            }
        }
        
        return chunks;
    }
    
    /**
     * 合并翻译结果
     */
    private IAIService.BilingualSummary mergeTranslationResults(List<IAIService.BilingualSummary> results) {
        IAIService.BilingualSummary merged = new IAIService.BilingualSummary();
        
        StringBuilder cnSummary = new StringBuilder();
        StringBuilder cnContent = new StringBuilder();
        StringBuilder cnSentences = new StringBuilder("[");
        
        StringBuilder enSummary = new StringBuilder();
        StringBuilder enContent = new StringBuilder();
        StringBuilder enSentences = new StringBuilder("[");
        
        int sentenceIndex = 0;
        
        for (IAIService.BilingualSummary result : results) {
            // 合并摘要（只取第一个结果的摘要）
            if (cnSummary.length() == 0 && result.getCnSummary() != null) {
                cnSummary.append(result.getCnSummary());
            }
            if (enSummary.length() == 0 && result.getEnSummary() != null) {
                enSummary.append(result.getEnSummary());
            }
            
            // 合并内容
            if (result.getCnContent() != null) {
                if (cnContent.length() > 0) cnContent.append("\n");
                cnContent.append(result.getCnContent());
            }
            if (result.getEnContent() != null) {
                if (enContent.length() > 0) enContent.append("\n");
                enContent.append(result.getEnContent());
            }
            
            // 合并句子（重新编号）
            if (result.getCnSentences() != null) {
                JSONArray cnArray = JSON.parseArray(result.getCnSentences());
                for (int i = 0; i < cnArray.size(); i++) {
                    if (cnSentences.length() > 1) cnSentences.append(",");
                    JSONObject sentence = cnArray.getJSONObject(i);
                    sentence.put("index", sentenceIndex++);
                    cnSentences.append(sentence.toString());
                }
            }
            
            if (result.getEnSentences() != null) {
                JSONArray enArray = JSON.parseArray(result.getEnSentences());
                for (int i = 0; i < enArray.size(); i++) {
                    if (enSentences.length() > 1) enSentences.append(",");
                    JSONObject sentence = enArray.getJSONObject(i);
                    sentence.put("index", i);
                    enSentences.append(sentence.toString());
                }
            }
        }
        
        cnSentences.append("]");
        enSentences.append("]");
        
        merged.setCnSummary(cnSummary.toString());
        merged.setCnContent(cnContent.toString());
        merged.setCnSentences(cnSentences.toString());
        
        merged.setEnSummary(enSummary.toString());
        merged.setEnContent(enContent.toString());
        merged.setEnSentences(enSentences.toString());
        
        return merged;
    }
    
    /**
     * 串行生成TTS音频（单Key + 断点续传）
     */
    private List<SentenceAudioResult> generateAudioSerial(JSONArray sentences, String language, ApiKeyPool keyPool, 
                                                          TTSEngineConfig ttsEngine, Long taskId, 
                                                          java.util.Map<Integer, SentenceAudioResult> savedSentences) {
        List<SentenceAudioResult> results = new ArrayList<>();
        
        try {
            // 创建TTS服务
            ITTSService ttsService = ttsServiceFactory.getService(ttsEngine.getEngineId());
            if (ttsService == null) {
                log.error("无法创建TTS服务");
                return results;
            }
            
            int delayMs = 500; // 每个请求之间延迟500ms
            
            for (int i = 0; i < sentences.size(); i++) {
                final int index = i;
                final String sentenceText = sentences.getJSONObject(i).getString("text");
                
                if (sentenceText == null || sentenceText.trim().isEmpty()) {
                    log.warn("跳过空句子: index={}", index);
                    continue;
                }
                
                // 🔥 检查该句子是否已经生成过
                if (savedSentences.containsKey(index)) {
                    log.info("句子 {}/{} 已存在TTS结果，跳过 ✓", index + 1, sentences.size());
                    results.add(savedSentences.get(index));
                    continue;
                }
                
                // 延迟（第一个句子除外）
                if (i > 0) {
                    try {
                        Thread.sleep(delayMs);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                
                try {
                    log.info("处理句子 {}/{} (Key: {}): {}", index + 1, sentences.size(), 
                            keyPool.getKeyName(), sentenceText.substring(0, Math.min(50, sentenceText.length())));
                    
                    ITTSService.TTSResult result = generateSpeechWithRetry(ttsService, sentenceText, language, 3);
                    
                    if (result != null && result.getFileMetadata() != null) {
                        SentenceAudioResult sentenceResult = new SentenceAudioResult(index, result);
                        results.add(sentenceResult);
                        
                        // 🔥 立即保存句子TTS结果（防止丢失）
                        saveSentenceTTS(taskId, language, index, result);
                        
                        log.info("句子{}/{}生成成功", index + 1, sentences.size());
                    } else {
                        log.error("句子{}生成失败", index + 1);
                    }
                } catch (Exception e) {
                    log.error("句子{}处理异常", index + 1, e);
                }
            }
        } finally {
            // 释放Key
            apiKeyPoolService.releaseKey(keyPool.getKeyId());
        }
        
        return results;
    }
    
    /**
     * 并行生成TTS音频（多Key + 断点续传）
     */
    private List<SentenceAudioResult> generateAudioParallel(JSONArray sentences, String language, List<ApiKeyPool> availableKeys, 
                                                            TTSEngineConfig ttsEngine, Long taskId,
                                                            java.util.Map<Integer, SentenceAudioResult> savedSentences) {
        List<SentenceAudioResult> results = new ArrayList<>();
        List<Future<SentenceAudioResult>> futures = new ArrayList<>();
        
        // 为每个句子分配一个Key并行处理
        for (int i = 0; i < sentences.size(); i++) {
            final int index = i;
            final String sentenceText = sentences.getJSONObject(i).getString("text");
            final ApiKeyPool keyPool = availableKeys.get(i % availableKeys.size());
            
            if (sentenceText == null || sentenceText.trim().isEmpty()) {
                log.warn("跳过空句子: index={}", index);
                continue;
            }
            
            // 🔥 检查该句子是否已经生成过
            if (savedSentences.containsKey(index)) {
                log.info("句子 {}/{} 已存在TTS结果，跳过 ✓", index + 1, sentences.size());
                // 使用CompletableFuture返回已保存的结果
                futures.add(CompletableFuture.completedFuture(savedSentences.get(index)));
                continue;
            }
            
            Future<SentenceAudioResult> future = parallelExecutor.submit(() -> {
                try {
                    log.info("句子 {}/{} 开始TTS生成 (Key: {})", index + 1, sentences.size(), keyPool.getKeyName());
                    
                    // 创建TTS服务（每个线程独立创建）
                    ITTSService ttsService = ttsServiceFactory.getService(ttsEngine.getEngineId());
                    if (ttsService == null) {
                        log.error("无法创建TTS服务");
                        return null;
                    }
                    
                    // 生成TTS
                    ITTSService.TTSResult result = generateSpeechWithRetry(ttsService, sentenceText, language, 3);
                    
                    if (result != null && result.getFileMetadata() != null) {
                        SentenceAudioResult sentenceResult = new SentenceAudioResult(index, result);
                        
                        // 🔥 立即保存句子TTS结果（防止丢失）
                        saveSentenceTTS(taskId, language, index, result);
                        
                        log.info("句子 {}/{} TTS生成成功", index + 1, sentences.size());
                        return sentenceResult;
                    } else {
                        log.error("句子 {}/{} TTS生成失败", index + 1, sentences.size());
                        return null;
                    }
                } catch (Exception e) {
                    log.error("句子 {}/{} TTS生成异常", index + 1, sentences.size(), e);
                    return null;
                }
            });
            
            futures.add(future);
        }
        
        // 收集所有结果
        for (int i = 0; i < futures.size(); i++) {
            try {
                SentenceAudioResult result = futures.get(i).get(3, TimeUnit.MINUTES);
                if (result != null) {
                    results.add(result);
                }
            } catch (TimeoutException e) {
                log.error("句子 {} TTS生成超时", i + 1);
            } catch (Exception e) {
                log.error("句子 {} 获取结果异常", i + 1, e);
            }
        }
        
        // 释放所有Keys
        for (ApiKeyPool keyPool : availableKeys) {
            apiKeyPoolService.releaseKey(keyPool.getKeyId());
        }
        
        return results;
    }

    // ==================== 任务队列管理方法 ====================

    /**
     * 创建任务记录
     * 
     * @param articleId 资讯ID
     * @param processMode 处理模式（summary/full）
     */
    private Long createTask(Long articleId, String processMode) {
        try {
            TaskQueue task = new TaskQueue();
            task.setArticleId(articleId);
            
            // 根据处理模式设置任务类型
            if ("full".equals(processMode)) {
                task.setTaskType("full_process");
            } else {
                task.setTaskType("summary_process");
            }
            
            task.setStatus("running");
            task.setTotalProgress(0);
            task.setStartedAt(DateUtils.getNowDate());
            task.setCreateTime(DateUtils.getNowDate());
            
            // 将处理模式保存到config中
            JSONObject config = new JSONObject();
            config.put("processMode", processMode);
            task.setConfig(config.toJSONString());
            
            taskQueueService.insertTaskQueue(task);
            log.info("任务创建成功: TaskID={}, ArticleID={}, 模式={}", task.getTaskId(), articleId, processMode);
            return task.getTaskId();
        } catch (Exception e) {
            log.error("创建任务记录失败: ArticleID={}, 模式={}", articleId, processMode, e);
            return null;
        }
    }

    /**
     * 更新任务状态
     */
    private void updateTaskStatus(Long taskId, String status, String errorMessage) {
        if (taskId == null) return;
        
        try {
            TaskQueue task = new TaskQueue();
            task.setTaskId(taskId);
            task.setStatus(status);
            
            if ("completed".equals(status)) {
                task.setTotalProgress(100);
                task.setCompletedAt(DateUtils.getNowDate());
            } else if ("failed".equals(status)) {
                task.setErrorMessage(errorMessage);
                task.setCompletedAt(DateUtils.getNowDate());
            }
            
            task.setUpdateTime(DateUtils.getNowDate());
            taskQueueService.updateTaskQueue(task);
            log.debug("任务状态更新: TaskID={}, Status={}", taskId, status);
        } catch (Exception e) {
            log.error("更新任务状态失败: TaskID={}", taskId, e);
        }
    }

    /**
     * 更新任务步骤（简化版：每次都插入新记录 + 更新总进度）
     */
    private void updateTaskStep(Long taskId, String stepName, String status, String errorMessage) {
        if (taskId == null) return;
        
        try {
            TaskStep step = new TaskStep();
            step.setTaskId(taskId);
            step.setStepName(stepName);
            step.setStepOrder(getStepOrder(stepName));
            step.setStatus(status);
            step.setErrorMessage(errorMessage);
            
            if ("running".equals(status)) {
                step.setStartedAt(DateUtils.getNowDate());
            } else if ("completed".equals(status) || "failed".equals(status)) {
                step.setCompletedAt(DateUtils.getNowDate());
            }
            
            step.setCreateTime(DateUtils.getNowDate());
            
            // 直接插入新的步骤记录（会覆盖旧记录或保留历史）
            taskQueueService.insertTaskStep(step);
            
            log.debug("任务步骤记录: TaskID={}, Step={}, Status={}", taskId, stepName, status);
            
            // 🔥 更新任务总进度
            updateTaskProgress(taskId);
        } catch (Exception e) {
            log.error("记录任务步骤失败: TaskID={}, Step={}", taskId, stepName, e);
        }
    }

    /**
     * 更新任务总进度（基于已完成的主步骤 + 处理模式）
     */
    private void updateTaskProgress(Long taskId) {
        if (taskId == null) return;
        
        try {
            // 获取任务配置，确定处理模式
            TaskQueue task = taskQueueService.selectTaskQueueByTaskId(taskId);
            if (task == null) return;
            
            String processMode = "summary"; // 默认
            if (task.getConfig() != null && !task.getConfig().isEmpty()) {
                try {
                    JSONObject config = JSONObject.parseObject(task.getConfig());
                    processMode = config.getString("processMode");
                    if (processMode == null || processMode.isEmpty()) {
                        processMode = "summary";
                    }
                } catch (Exception e) {
                    log.warn("解析任务配置失败，使用默认模式: {}", e.getMessage());
                }
            }
            
            List<TaskStep> steps = taskQueueService.selectTaskStepsByTaskId(taskId);
            
            // 根据处理模式定义不同的步骤权重
            java.util.Map<String, Integer> stepWeights = new java.util.HashMap<>();
            
            if ("full".equals(processMode)) {
                // 全文模式：TTS权重更高（因为内容更长）
                stepWeights.put("clean_data", 5);
                stepWeights.put("ai_translate", 35);
                stepWeights.put("save_content", 10);
                stepWeights.put("tts_generate_cn", 25);
                stepWeights.put("tts_generate_en", 25);
            } else {
                // 摘要模式：AI权重更高，TTS权重较低（内容较短）
                stepWeights.put("clean_data", 10);
                stepWeights.put("ai_translate", 50);
                stepWeights.put("save_content", 10);
                stepWeights.put("tts_generate_cn", 15);
                stepWeights.put("tts_generate_en", 15);
            }
            
            int totalProgress = 0;
            
            for (java.util.Map.Entry<String, Integer> entry : stepWeights.entrySet()) {
                String stepName = entry.getKey();
                Integer weight = entry.getValue();
                
                // 查找该步骤的最新记录
                TaskStep latestStep = null;
                for (TaskStep step : steps) {
                    if (stepName.equals(step.getStepName())) {
                        if (latestStep == null || step.getCreateTime().after(latestStep.getCreateTime())) {
                            latestStep = step;
                        }
                    }
                }
                
                // 如果步骤已完成，累加权重
                if (latestStep != null && "completed".equals(latestStep.getStatus())) {
                    totalProgress += weight;
                }
            }
            
            // 更新任务进度
            TaskQueue updateTask = new TaskQueue();
            updateTask.setTaskId(taskId);
            updateTask.setTotalProgress(totalProgress);
            updateTask.setUpdateTime(DateUtils.getNowDate());
            taskQueueService.updateTaskQueue(updateTask);
            
            log.debug("任务进度更新: TaskID={}, 模式={}, Progress={}%", taskId, processMode, totalProgress);
        } catch (Exception e) {
            log.error("更新任务进度失败: TaskID={}", taskId, e);
        }
    }

    /**
     * 获取步骤顺序
     */
    private Integer getStepOrder(String stepName) {
        switch (stepName) {
            case "clean_data": return 1;
            case "ai_translate": return 2;
            case "save_content": return 3;
            case "tts_generate_cn": return 4;
            case "tts_generate_en": return 5;
            default: return 99;
        }
    }

    /**
     * 恢复中断的任务（断点续传）
     */
    @Async
    public void resumeTask(Long taskId) {
        log.info("========== 恢复任务 ========== TaskID: {}", taskId);
        
        try {
            // 1. 获取任务信息
            TaskQueue task = taskQueueService.selectTaskQueueByTaskId(taskId);
            if (task == null) {
                log.error("任务不存在: TaskID={}", taskId);
                return;
            }
            
            Long articleId = task.getArticleId();
            log.info("任务对应的文章ID: {}", articleId);
            
            // 2. 获取已完成的步骤
            List<TaskStep> steps = taskQueueService.selectTaskStepsByTaskId(taskId);
            boolean cleanDataCompleted = isStepCompleted(steps, "clean_data");
            boolean aiTranslateCompleted = isStepCompleted(steps, "ai_translate");
            boolean saveContentCompleted = isStepCompleted(steps, "save_content");
            boolean ttsGenerateCnCompleted = isStepCompleted(steps, "tts_generate_cn");
            boolean ttsGenerateEnCompleted = isStepCompleted(steps, "tts_generate_en");
            
            log.info("任务进度检查:");
            log.info("  - clean_data: {}", cleanDataCompleted ? "✓" : "✗");
            log.info("  - ai_translate: {}", aiTranslateCompleted ? "✓" : "✗");
            log.info("  - save_content: {}", saveContentCompleted ? "✓" : "✗");
            log.info("  - tts_generate_cn: {}", ttsGenerateCnCompleted ? "✓" : "✗");
            log.info("  - tts_generate_en: {}", ttsGenerateEnCompleted ? "✓" : "✗");
            
            // 3. 获取文章信息和处理模式
            NewsArticle article = newsArticleService.selectNewsArticleByArticleId(articleId);
            if (article == null) {
                log.error("文章不存在: {}", articleId);
                updateTaskStatus(taskId, "failed", "文章不存在");
                return;
            }
            
            // 从任务配置中读取处理模式（优先），否则从文章中读取
            String processMode = "summary"; // 默认
            if (task.getConfig() != null && !task.getConfig().isEmpty()) {
                try {
                    JSONObject config = JSONObject.parseObject(task.getConfig());
                    processMode = config.getString("processMode");
                    if (processMode == null || processMode.isEmpty()) {
                        processMode = (article.getProcessMode() == null || article.getProcessMode().isEmpty()) ? "summary" : article.getProcessMode();
                    }
                } catch (Exception e) {
                    log.warn("解析任务配置失败，从文章获取处理模式: {}", e.getMessage());
                    processMode = (article.getProcessMode() == null || article.getProcessMode().isEmpty()) ? "summary" : article.getProcessMode();
                }
            } else {
                processMode = (article.getProcessMode() == null || article.getProcessMode().isEmpty()) ? "summary" : article.getProcessMode();
            }
            
            log.info("任务处理模式: {}", processMode);
            
            // 4. 根据已完成的步骤决定从哪里继续
            
            // 4.1 如果翻译未完成，从翻译开始
            if (!aiTranslateCompleted || !saveContentCompleted) {
                log.info("[断点续传] 从AI翻译步骤开始");
                updateArticleStatus(articleId, "1");
                updateTaskStep(taskId, "clean_data", "completed", null); // 标记清理完成
                
                // 清理旧数据
                cleanOldData(articleId);
                
                // 重新执行翻译
                updateTaskStep(taskId, "ai_translate", "running", null);
                String originalContent = article.getContent();
                if (originalContent == null || originalContent.trim().isEmpty()) {
                    log.error("资讯内容为空");
                    updateTaskStatus(taskId, "failed", "资讯内容为空");
                    updateArticleStatus(articleId, "4");
                    return;
                }
                
                // 清理文本
                originalContent = cleanContentText(originalContent);
                
                // 执行翻译（使用之前确定的processMode）
                IAIService.BilingualSummary bilingualResult = null;
                if ("full".equals(processMode)) {
                    bilingualResult = processWithMultipleModels(taskId, articleId, originalContent, processMode);
                } else {
                    IAIService aiService = aiServiceFactory.getDefaultService();
                    if (aiService != null) {
                        bilingualResult = aiService.generateBilingualSummary(originalContent, "cn", processMode);
                    }
                }
                
                if (bilingualResult == null) {
                    log.error("翻译失败");
                    updateTaskStep(taskId, "ai_translate", "failed", "翻译失败");
                    updateTaskStatus(taskId, "failed", "翻译失败");
                    updateArticleStatus(articleId, "4");
                    return;
                }
                
                updateTaskStep(taskId, "ai_translate", "completed", null);
                
                // 保存内容
                updateTaskStep(taskId, "save_content", "running", null);
                saveTranslatedContent(articleId, article.getArticleType(), bilingualResult);
                updateTaskStep(taskId, "save_content", "completed", null);
                
                saveContentCompleted = true;
            } else {
                log.info("[断点续传] 翻译和内容保存已完成，跳过");
            }
            
            // 4.2 如果中文TTS未完成，生成中文TTS
            if (saveContentCompleted && !ttsGenerateCnCompleted) {
                log.info("[断点续传] 从中文TTS生成步骤开始");
                updateArticleStatus(articleId, "2");
                updateTaskStep(taskId, "tts_generate_cn", "running", null);
                
                boolean cnSuccess = false;
                try {
                    cnSuccess = generateAudio(articleId, "cn", taskId);
                    if (cnSuccess) {
                        updateTaskStep(taskId, "tts_generate_cn", "completed", null);
                    } else {
                        updateTaskStep(taskId, "tts_generate_cn", "failed", "中文TTS生成失败");
                    }
                } catch (Exception e) {
                    log.error("中文TTS生成异常", e);
                    updateTaskStep(taskId, "tts_generate_cn", "failed", "中文TTS生成异常: " + e.getMessage());
                }
                
                ttsGenerateCnCompleted = cnSuccess;
            } else if (ttsGenerateCnCompleted) {
                log.info("[断点续传] 中文TTS已完成，跳过");
            }
            
            // API冷却
            if (ttsGenerateCnCompleted && !ttsGenerateEnCompleted) {
                try {
                    log.info("API冷却时间：10秒");
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            
            // 4.3 如果英文TTS未完成，生成英文TTS
            if (ttsGenerateCnCompleted && !ttsGenerateEnCompleted) {
                log.info("[断点续传] 从英文TTS生成步骤开始");
                updateTaskStep(taskId, "tts_generate_en", "running", null);
                
                boolean enSuccess = false;
                try {
                    enSuccess = generateAudio(articleId, "en", taskId);
                    if (enSuccess) {
                        updateTaskStep(taskId, "tts_generate_en", "completed", null);
                    } else {
                        updateTaskStep(taskId, "tts_generate_en", "failed", "英文TTS生成失败");
                    }
                } catch (Exception e) {
                    log.error("英文TTS生成异常", e);
                    updateTaskStep(taskId, "tts_generate_en", "failed", "英文TTS生成异常: " + e.getMessage());
                }
                
                ttsGenerateEnCompleted = enSuccess;
            } else if (ttsGenerateEnCompleted) {
                log.info("[断点续传] 英文TTS已完成，跳过");
            }
            
            // 5. 所有步骤完成
            if (ttsGenerateCnCompleted && ttsGenerateEnCompleted) {
                log.info("========== 任务恢复完成 ========== TaskID: {}", taskId);
                updateArticleStatus(articleId, "3");
                updateTaskStatus(taskId, "completed", null);
            } else {
                log.warn("========== 任务恢复未完全成功 ========== TaskID: {}", taskId);
                updateTaskStatus(taskId, "failed", "部分步骤失败");
                updateArticleStatus(articleId, "4");
            }
            
        } catch (Exception e) {
            log.error("========== 任务恢复失败 ========== TaskID: {}", taskId, e);
            updateTaskStatus(taskId, "failed", "恢复异常: " + e.getMessage());
        }
    }

    /**
     * 检查步骤是否已完成
     */
    private boolean isStepCompleted(List<TaskStep> steps, String stepName) {
        if (steps == null || steps.isEmpty()) {
            return false;
        }
        
        // 查找该步骤的最新记录
        TaskStep latestStep = null;
        for (TaskStep step : steps) {
            if (stepName.equals(step.getStepName())) {
                if (latestStep == null || step.getCreateTime().after(latestStep.getCreateTime())) {
                    latestStep = step;
                }
            }
        }
        
        return latestStep != null && "completed".equals(latestStep.getStatus());
    }

    /**
     * 保存翻译内容（抽取为独立方法，便于复用）
     */
    private void saveTranslatedContent(Long articleId, String articleType, IAIService.BilingualSummary bilingualResult) {
        // 保存中文内容
        NewsContent chineseContent = new NewsContent();
        chineseContent.setArticleId(articleId);
        chineseContent.setArticleType(articleType);
        chineseContent.setLanguage("cn");
        chineseContent.setSummary(bilingualResult.getCnSummary());
        chineseContent.setContent(bilingualResult.getCnContent());
        chineseContent.setSentences(bilingualResult.getCnSentences());
        chineseContent.setCreateTime(DateUtils.getNowDate());
        newsContentService.insertNewsContent(chineseContent);
        log.info("中文内容保存成功，ContentID: {}", chineseContent.getContentId());
        
        // 保存英文内容
        NewsContent englishContent = new NewsContent();
        englishContent.setArticleId(articleId);
        englishContent.setArticleType(articleType);
        englishContent.setLanguage("en");
        englishContent.setSummary(bilingualResult.getEnSummary());
        englishContent.setContent(bilingualResult.getEnContent());
        englishContent.setSentences(bilingualResult.getEnSentences());
        englishContent.setCreateTime(DateUtils.getNowDate());
        newsContentService.insertNewsContent(englishContent);
        log.info("英文内容保存成功，ContentID: {}", englishContent.getContentId());
    }

    /**
     * 保存单个块的翻译结果（增量保存，防止数据丢失）
     * 保存位置：tzai_task_step 表的 output_data 字段
     */
    private void saveChunkTranslation(Long taskId, int chunkIndex, IAIService.BilingualSummary result) {
        if (taskId == null) return;
        
        try {
            // 构建JSON数据
            JSONObject chunkData = new JSONObject();
            chunkData.put("chunk_index", chunkIndex);
            chunkData.put("cn_content", result.getCnContent());
            chunkData.put("en_content", result.getEnContent());
            chunkData.put("cn_sentences", result.getCnSentences());
            chunkData.put("en_sentences", result.getEnSentences());
            chunkData.put("saved_at", System.currentTimeMillis());
            
            // 创建任务步骤记录（每个块一条记录）
            TaskStep step = new TaskStep();
            step.setTaskId(taskId);
            step.setStepName("ai_translate_chunk_" + chunkIndex);
            step.setStepOrder(100 + chunkIndex); // 使用100+索引作为顺序
            step.setStatus("completed");
            step.setOutputData(chunkData.toString());
            step.setStartedAt(DateUtils.getNowDate());
            step.setCompletedAt(DateUtils.getNowDate());
            step.setCreateTime(DateUtils.getNowDate());
            
            taskQueueService.insertTaskStep(step);
            log.debug("块 {} 翻译结果已保存到任务步骤", chunkIndex);
        } catch (Exception e) {
            log.error("保存块 {} 翻译结果失败（不影响主流程）", chunkIndex, e);
        }
    }

    /**
     * 加载已保存的块翻译结果（用于断点续传）
     * 返回：Map<块索引, 翻译结果>
     */
    private java.util.Map<Integer, IAIService.BilingualSummary> loadSavedChunkTranslations(Long taskId) {
        java.util.Map<Integer, IAIService.BilingualSummary> savedChunks = new java.util.HashMap<>();
        
        if (taskId == null) return savedChunks;
        
        try {
            List<TaskStep> steps = taskQueueService.selectTaskStepsByTaskId(taskId);
            for (TaskStep step : steps) {
                String stepName = step.getStepName();
                if (stepName != null && stepName.startsWith("ai_translate_chunk_") && "completed".equals(step.getStatus())) {
                    try {
                        // 提取块索引
                        int chunkIndex = Integer.parseInt(stepName.substring("ai_translate_chunk_".length()));
                        
                        // 解析输出数据
                        String outputData = step.getOutputData();
                        if (outputData != null && !outputData.isEmpty()) {
                            JSONObject chunkData = JSONObject.parseObject(outputData);
                            
                            // 重建BilingualSummary对象
                            IAIService.BilingualSummary result = new IAIService.BilingualSummary();
                            result.setCnContent(chunkData.getString("cn_content"));
                            result.setEnContent(chunkData.getString("en_content"));
                            
                            // 处理JSONArray -> String (BilingualSummary使用String类型存储JSONArray)
                            String cnSentences = chunkData.getString("cn_sentences");
                            String enSentences = chunkData.getString("en_sentences");
                            result.setCnSentences(cnSentences);
                            result.setEnSentences(enSentences);
                            
                            savedChunks.put(chunkIndex, result);
                            log.info("加载块 {} 的已保存翻译结果", chunkIndex);
                        }
                    } catch (Exception e) {
                        log.warn("解析块翻译结果失败: {}", stepName, e);
                    }
                }
            }
            
            if (!savedChunks.isEmpty()) {
                log.info("共加载 {} 个已保存的块翻译结果", savedChunks.size());
            }
        } catch (Exception e) {
            log.error("加载已保存的块翻译结果失败", e);
        }
        
        return savedChunks;
    }

    /**
     * 保存单个句子的TTS结果（增量保存，防止数据丢失）
     * 保存位置：tzai_task_step 表的 output_data 字段
     */
    private void saveSentenceTTS(Long taskId, String language, int sentenceIndex, ITTSService.TTSResult result) {
        if (taskId == null) return;
        
        try {
            // 构建JSON数据
            JSONObject sentenceData = new JSONObject();
            sentenceData.put("sentence_index", sentenceIndex);
            sentenceData.put("language", language);
            sentenceData.put("file_metadata", result.getFileMetadata());
            sentenceData.put("audio_url", result.getAudioUrl());
            sentenceData.put("duration", result.getDuration());
            sentenceData.put("timestamps", result.getTimestamps());
            sentenceData.put("file_size", result.getFileSize());
            sentenceData.put("saved_at", System.currentTimeMillis());
            
            // 创建任务步骤记录（每个句子一条记录）
            TaskStep step = new TaskStep();
            step.setTaskId(taskId);
            step.setStepName("tts_sentence_" + language + "_" + sentenceIndex);
            step.setStepOrder(200 + sentenceIndex); // 使用200+索引作为顺序
            step.setStatus("completed");
            step.setOutputData(sentenceData.toString());
            step.setStartedAt(DateUtils.getNowDate());
            step.setCompletedAt(DateUtils.getNowDate());
            step.setCreateTime(DateUtils.getNowDate());
            
            taskQueueService.insertTaskStep(step);
            log.debug("句子 {} ({}) TTS结果已保存到任务步骤", sentenceIndex, language);
        } catch (Exception e) {
            log.error("保存句子 {} ({}) TTS结果失败（不影响主流程）", sentenceIndex, language, e);
        }
    }

    /**
     * 加载已保存的句子TTS结果（用于断点续传）
     * 返回：Map<句子索引, TTS结果>
     */
    private java.util.Map<Integer, SentenceAudioResult> loadSavedSentenceTTS(Long taskId, String language) {
        java.util.Map<Integer, SentenceAudioResult> savedSentences = new java.util.HashMap<>();
        
        if (taskId == null) return savedSentences;
        
        try {
            List<TaskStep> steps = taskQueueService.selectTaskStepsByTaskId(taskId);
            String stepPrefix = "tts_sentence_" + language + "_";
            
            for (TaskStep step : steps) {
                String stepName = step.getStepName();
                if (stepName != null && stepName.startsWith(stepPrefix) && "completed".equals(step.getStatus())) {
                    try {
                        // 提取句子索引
                        int sentenceIndex = Integer.parseInt(stepName.substring(stepPrefix.length()));
                        
                        // 解析输出数据
                        String outputData = step.getOutputData();
                        if (outputData != null && !outputData.isEmpty()) {
                            JSONObject sentenceData = JSONObject.parseObject(outputData);
                            
                            // 重建TTSResult对象
                            ITTSService.TTSResult result = new ITTSService.TTSResult();
                            result.setFileMetadata(sentenceData.getString("file_metadata"));
                            result.setAudioUrl(sentenceData.getString("audio_url"));
                            result.setDuration(sentenceData.getDouble("duration"));
                            result.setTimestamps(sentenceData.getString("timestamps"));
                            result.setFileSize(sentenceData.getLong("file_size"));
                            
                            // 包装为SentenceAudioResult
                            SentenceAudioResult sentenceResult = new SentenceAudioResult(sentenceIndex, result);
                            savedSentences.put(sentenceIndex, sentenceResult);
                            
                            log.info("加载句子 {} ({}) 的已保存TTS结果", sentenceIndex, language);
                        }
                    } catch (Exception e) {
                        log.warn("解析句子TTS结果失败: {}", stepName, e);
                    }
                }
            }
            
            if (!savedSentences.isEmpty()) {
                log.info("共加载 {} 个已保存的句子TTS结果 ({})", savedSentences.size(), language);
            }
        } catch (Exception e) {
            log.error("加载已保存的句子TTS结果失败", e);
        }
        
        return savedSentences;
    }
}

